﻿using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading;

namespace Quickuse.Logging.EmailLogger
{
    /// <summary>
    /// EmailLoggerProcessor
    /// </summary>
    internal class EmailLoggerProcessor : IDisposable
    {

        private readonly BlockingCollection<string> _messageQueue;
        private readonly Thread _outputEmailThread;

        private IEmailLoggerSettings _emailLoggerSettings;

        public EmailLoggerProcessor(IEmailLoggerSettings emailLoggerSettings)
        {
            _emailLoggerSettings = emailLoggerSettings;
            _messageQueue = new BlockingCollection<string>(_emailLoggerSettings.MaxQueuedLength);

            _outputEmailThread = new Thread(ProcessLogQueue)
            {
                IsBackground = true,
                Name = "Email logger queue processing thread"
            };

            _outputEmailThread.Start();

            if (_emailLoggerSettings.ChangeToken != null)
            {
                _emailLoggerSettings.ChangeToken.RegisterChangeCallback(OnConfigurationReload, null);
            }
        }

        public void OnConfigurationReload(object state)
        {
            try
            {
                _emailLoggerSettings = _emailLoggerSettings.Reload();
            }
            catch (Exception ex)
            {
                InternalLog.Error($" while loading configuration changes.{Environment.NewLine}{ex}");
            }
            finally
            {
                // The token will change each time it reloads, so we need to register again.
                if (_emailLoggerSettings?.ChangeToken != null)
                {
                    _emailLoggerSettings.ChangeToken.RegisterChangeCallback(OnConfigurationReload, null);
                }
            }
        }

        /// <summary>
        /// 入队消息
        /// </summary>
        /// <param name="message">message</param>
        /// <param name="logLevel">logLevel</param>
        internal void EnqueueMessage(string message, LogLevel logLevel)
        {
            if (_emailLoggerSettings.IsAsyn && !_messageQueue.IsAddingCompleted)
            {
                _messageQueue.Add(message);
            }
            else
            {
                SendMail(message);
            }
        }

        //处理消息
        public void ProcessLogQueue()
        {
            try
            {
                foreach (var message in _messageQueue.GetConsumingEnumerable())
                {
                    SendMail(message);
                }
            }
            catch
            {
                try
                {
                    _messageQueue.CompleteAdding();
                }
                catch { }
            }
        }

        /// <summary>
        /// SendMail
        /// </summary>
        /// <param name="logMessage">logMessage</param>
        private void SendMail(string logMessage)
        {
            try
            {
                if (string.IsNullOrEmpty(_emailLoggerSettings.MailServer)
                    || string.IsNullOrEmpty(_emailLoggerSettings.MailAccount)
                    || string.IsNullOrEmpty(_emailLoggerSettings.MailPassword)
                    || string.IsNullOrEmpty(_emailLoggerSettings.FromMail)
                    || string.IsNullOrEmpty(_emailLoggerSettings.ToMails))
                {
                    return;
                }

                using (SmtpClient smtpClient = new SmtpClient(_emailLoggerSettings.MailServer))
                {
                    smtpClient.UseDefaultCredentials = false;
                    smtpClient.Credentials = new NetworkCredential(_emailLoggerSettings.MailAccount, _emailLoggerSettings.MailPassword);
                    smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
                    using (MailMessage mailMessage = new MailMessage(_emailLoggerSettings.FromMail, _emailLoggerSettings.ToMails))
                    {
                        mailMessage.Subject = Subject;
                        mailMessage.Body = logMessage;
                        smtpClient.Send(mailMessage);
                    }
                }
            }
            catch (Exception ex)
            {
                InternalLog.Error($"send email error:{ex}");
                throw ex;
            }
        }

        #region Subject
        /// <summary>
        /// Subject
        /// </summary>
        private string Subject
        {
            get
            {
                if (string.IsNullOrEmpty(_emailLoggerSettings.ClusterSeq))
                {
                    return _emailLoggerSettings.SubjectName;
                }
                else
                {
                    return $"{_emailLoggerSettings.SubjectName}_{_emailLoggerSettings.ClusterSeq}";
                }
            }
        }
        #endregion

        public void Dispose()
        {
            _messageQueue.CompleteAdding();
            try
            {
                _outputEmailThread.Join(_emailLoggerSettings.TaskWaitTime); // with timeout in-case Console is locked by user input
            }
            catch (ThreadStateException ex)
            {
                InternalLog.Error(ex);
            }
        }
    }
}
